#
# Introduction
# ------------
#
# ITK factories can be registered automatically either "statically" or "dynamically".
#
# "statically" : This is the mechanism described below and supported by UseITK.
#
# "dynamically": This corresponds to the runtime loading of shared libraries
#                exporting the "itkLoad" symbol and available in directory
#                associated with the `ITK_AUTOLOAD_PATH` environment variable.
#
#
# Overview: Static registration of ITK factories
# ----------------------------------------------
#
# For each factory type (Image, Transform, Mesh, FFTImageFilterInit, ...), a
# registration manager header named
# `itk<factory_type>FactoryRegisterManager.h` is configured.
#
# The registration manager header is itself included at the end of the Reader,
# Writer and FFT headers for each factory types. It will ensure all the factories
# associated with the different file formats are registered only once by
# relying on static initialization of a global variable properly initialized
# by the loader across all translation units.
#
# By including `itk<factory_type>FileReader.h` or `itk<factory_type>FileWriter.h`
# header in user code, the corresponding IO factories will be ensured to be
# registered globally and available across translation units.
#
# The file formats associated with each IO factory type are hard-coded as
# a list in a CMake variable named after `LIST_OF_<factory_type>IO_FORMATS`
# generated by this file.
#
# "Init factories" (such as "VnlFFTImageFilterInitFactory") are intended to reduce
# overhead. They have the sole purpose of registering multiple factories with the
# object factory singleton at once. Init factories do not inherit from
# ObjectFactoryBase, cannot define initialization overrides, and will never be
# added to the global singleton list of object factories.
#
# For the FFTImageFilterInitFactory classes, each FFT filter which has a factory needs to
# be included in its initialization list.
#
#
#
# Registration manager header
# ---------------------------
#
# Configuration of the header requires two CMake variables to be set. The
# two variables `LIST_OF_FACTORY_NAMES` and `LIST_OF_FACTORIES_REGISTRATION` will
# respectively contain a list of function declaration and calls. The associated
# function names are of the form `<factory_name>FactoryRegister__Private`.
#
#
# Disabling static registration
# -----------------------------
#
# Setting variable `ITK_NO_<factory_name_uc>_FACTORY_REGISTER_MANAGER` to `ON` prior calling
# `include(${ITK_USE_FILE})` disables the static registration.
# Here <factory_name_uc> is the upper case name of the factory. For example,
# `IMAGEIO`, `MESHIO`, `TRANSFORMIO`, or `FFTIMAGEFILTERINIT`.
#
# All factories can be disabled with the following CMake code:
#  foreach(_factory_name ${ITK_FACTORY_LIST})
#    string(TOLOWER ${_factory_name} _f)
#    set ( ITK_NO_${_factory_name}_FACTORY_REGISTER_MANAGER 1)
#  endforeach()
#
#
# IO format lists
# ---------------
#
# The IO format lists are CMake variables initialized when loading ITK modules.
# Each module is responsible for declaration of the image and transform formats that it
# implements.
#
# One list will be set for each factory type.
#
# Variable name is expected to be of the form `LIST_OF_<factory_type>IO_FORMATS`
# where `<factory_type>` is the upper-cased `factory_type`.
#
# Notes:
#
#  * The order of file format matters: Since it will define in which order
#    the different factories are registered, it will by extension defines the
#    "priority" for each file format.
#
#  * This list does not indicates which factory are registered: It should be
#    considered as a hint to indicate the order in which factory should
#    registered based on which ITK modules are used or imported within a
#    given project.
#
#
# The "Private" function
# ----------------------
#
# The function is responsible to register the IO factory for a given format
# associated with a specific factory type.
#
# It is defined in the `itk<format><factory_type>IOFactory.cxx` file and should
# internally call the function `<format><factory_type>IOFactory::RegisterOneFactory()`.
#
# The factory is then effectively registered in `RegisterOneFactory()` by calling
# `ObjectFactoryBase::RegisterFactoryInternal(<format><factory_type>IOFactory::New())`.
#
# Notes:
#
#  * the function will be available in both shared or static build case. Static
#    initialization will happen in both cases.
#
#  * the function is unique and consistently named for each IO factory. The
#    current naming convention is an implementation detail and is not part
#    of the ITK Public API.
#
#
# Generation of "Private()" function lists
# ----------------------------------------
#
# The configuration of the registration header for each factory is done
# using the convenience function
# `itk_generate_factory_registration()`.
#
# If `UseITK.cmake` is not included into a project this CMake method
# may be called to generate the `Private()` functions.
#
#
# By iterating over the format list, the CMake function `_itk_configure_FactoryRegisterManager()`
# will itself call `_itk_ADD_FACTORY_REGISTRATION()` to generate the Private function
# names and update the `LIST_OF_FACTORY_NAMES` and `LIST_OF_FACTORIES_REGISTRATION`
# CMake lists.
#
# Every file format is associated with a module name and factory name set
# by iterating over the list of file format prior the call to `_itk_configure_FactoryRegisterManager()`.
#
#
# Caveats
# -------
#
# Since the both include directory containing the registration manager headers
# and the `ITK_IO_FACTORY_REGISTER_MANAGER` COMPILE_DEFINITIONS are set as
# directory properties, including external project (themselves including ITK)
# after including ITK can have unintended side effects.
#

# _itk_configure_FactoryRegisterManager(<factory_type> <formats>)
#
# Configure the registration manager header in the directory
# `<CMAKE_CURRENT_BINARY_DIR>/ITKFactoryRegistration/`.
#
# Header is named using the template `itk<factory_type>FactoryRegisterManager.h`
#
function(_itk_configure_FactoryRegisterManager factory_type formats)
  set(LIST_OF_FACTORIES_REGISTRATION "")
  set(LIST_OF_FACTORY_NAMES "")
  string(TOLOWER ${factory_type} _qualifier)
  foreach(format ${formats})
    set(_module_name ${${format}_${_qualifier}_module_name})
    set(_factory_name ${${format}_${_qualifier}_factory_name})
    _itk_add_factory_registration(
          "LIST_OF_FACTORIES_REGISTRATION"
          "LIST_OF_FACTORY_NAMES"
          ${_module_name}
          ${_factory_name}
    )
  endforeach()

  configure_file(
    ${ITK_CMAKE_DIR}/itk${factory_type}FactoryRegisterManager.h.in
    "${CMAKE_CURRENT_BINARY_DIR}/ITKFactoryRegistration/itk${factory_type}FactoryRegisterManager.h"
    @ONLY
  )
endfunction()

# _itk_ADD_FACTORY_REGISTRATION(<registration_list_var> <names_list_var> <module_name> <factory_name>)
#
# Update variables`LIST_OF_FACTORY_NAMES` and `LIST_OF_FACTORIES_REGISTRATION`
# used to configure `itk<factory_type>IOFactoryRegisterManager.h`.
#
macro(
  _itk_ADD_FACTORY_REGISTRATION
  _registration_list_var
  _names_list_var
  _module_name
  _factory_name
)
  # note: this is an internal CMake variable and should not be used outside ITK
  set(_abi)
  if(${_module_name}_ENABLE_SHARED AND ITK_BUILD_SHARED)
    set(_abi "ITK_ABI_IMPORT")
  endif()

  set(
    ${_registration_list_var}
    "${${_registration_list_var}}void ${_abi} ${_factory_name}FactoryRegister__Private();"
  )
  set(
    ${_names_list_var}
    "${${_names_list_var}}${_factory_name}FactoryRegister__Private,"
  )
endmacro()

#-----------------------------------------------------------------------------
# Factory registration
#
# Arguments are factory names of factory registration mangers to be
# generated, if no arguments are provided then ITK_FACTORY_LIST is
# used for the list of managers generated.
#
# Output cmake variables : LIST_OF_{FACTORY}_FORMATS
#-----------------------------------------------------------------------------
macro(itk_generate_factory_registration)
  set(_factory_list ${ITK_FACTORY_LIST})

  set(variadic_args ${ARGN})
  list(LENGTH variadic_args _argc)
  if(${_argc} GREATER 0)
    set(_factory_list ${variadic_args})
  endif()

  foreach(_factory_name ${_factory_list})
    string(TOUPPER ${_factory_name} factory_uc)
    string(TOLOWER ${_factory_name} factory_lc)
    set(LIST_OF_${factory_uc}_FORMATS "")
    foreach(_format ${ITK_${_factory_name}})
      set(Module)
      foreach(_module ${ITK_FACTORY_NAMES})
        string(
          REGEX
          MATCH
          "^.*::${_factory_name}::${_format}$"
          Module_Matched
          "${_module}"
        )
        if(Module_Matched)
          string(
            REGEX
            REPLACE
            "(.*)::${_factory_name}::${_format}"
            "\\1"
            Module
            "${Module_Matched}"
          )
          break()
        endif()
      endforeach()
      unset(_module)
      if(NOT Module)
        message(
          FATAL_ERROR
          "Module not found for ${_factory_name} format \"${_format}\" in factory"
        )
      endif()
      list(APPEND LIST_OF_${factory_uc}_FORMATS ${_format})
      set(${_format}_${factory_lc}_module_name ${Module})
      set(${_format}_${factory_lc}_factory_name ${_format}${_factory_name})
    endforeach()
    unset(_format)
    if(ITK_NO_${factory_uc}_FACTORY_REGISTER_MANAGER)
      # pass generation this factory registration
    elseif(_factory_name MATCHES "IO" AND ITK_NO_IO_FACTORY_REGISTER_MANAGER)
      message(
        WARNING
        "ITK_NO_IO_FACTORY_REGISTER_MANAGER CMake variable is "
        "deprecated. Use ITK_NO_${factory_uc}_FACTORY_REGISTER_MANAGER"
      )
    else()
      _itk_configure_factoryregistermanager("${_factory_name}" "${LIST_OF_${factory_uc}_FORMATS}")
    endif()
  endforeach()
  unset(_factory_name)
endmacro()
